
#include "adfile.h"
#include <sys/stat.h>
#include <sys/time.h>		/** for gettimeofday **/
#include <pcap.h>
#include <fcntl.h>
#include <arpa/inet.h>
#include <stdio.h>		/** for printf **/
#include <stdlib.h>		/** for atoi **/
#include <string.h>		/** for strlen **/

#define PATHSWITCH 1
#define PATHCONTINUE 0

void ad_file_t::dump(void)
{
#if defined (__i386__)
	printf("fd: %d, file: %s, size: %u\n", fd, path.c_str(), size);
#else
	printf("fd: %d, file: %s, size: %lu\n", fd, path.c_str(), size);
#endif
}

/* @Return */
static int ck_cur_st(cur_st_t & cur_st)
{
	int rtn = PATHCONTINUE;
	struct tm *tm, t;
	time_t now = time(NULL);
	if ((tm = localtime_r(&now, &t)) == NULL) {
		memset(&cur_st, 0x00, sizeof(cur_st));
		return PATHSWITCH;
	}
	if ( cur_st.cur_year != tm->tm_year + 1900 ){
		cur_st.cur_year = tm->tm_year + 1900;
		rtn = PATHSWITCH;
	}
	if ( cur_st.cur_mon != tm->tm_mon + 1 ){
		cur_st.cur_mon = tm->tm_mon + 1;
		rtn = PATHSWITCH;
	}
	if ( cur_st.cur_day != tm->tm_mday ){
		cur_st.cur_day = tm->tm_mday;
		rtn = PATHSWITCH;
	}
	if ( cur_st.cur_hour != tm->tm_hour ){
		cur_st.cur_hour = tm->tm_hour;
		rtn = PATHSWITCH;
	}
	cur_st.cur_min = tm->tm_min;
	cur_st.cur_sec = tm->tm_sec;

	return rtn;
}

ad_file_t::ad_file_t()
{
	fd = -1;
	path = "";
	size = 0;
	ck_cur_st(cur_st);
	dump_pcap_init();
}

/**
 * 读取配置时调用，设置文件信息
 * @IN: 
 *	p:  path
 *	s:  size
 **/
void ad_file_t::set_file_info(const char *p, const char *s)
{
	int len = strlen(s);

	path = p;
	size = atoi(s);

	if (s[len-1] == 'k' || s[len-1] == 'K') {
		size *= 1024;
	} else if (s[len-1] == 'm' || s[len-1] == 'M') {
		size *= 1024 * 1024;
	} else if (s[len-1] == 'g' || s[len-1] == 'G') {
		size *= 1024 * 1024 * 1024;
	}
}


/**
 * 检查路径是否存在，不存在则创建
 **/
int ad_file_t::check_path(std::string &path)
{
	int pos;
	struct stat st;

	if (path == "/") {
		return 0;
	}

	if ((pos = path.rfind("/")) == -1) {
		return -1;
	}

	if (stat(path.c_str(), &st) != 0) {
		std::string p = path.substr(0, pos);
		check_path(p);
		mkdir(path.c_str(), 0755);
	}
	return 0;
}

/**
 * 打开文件
 **/
int ad_file_t::open_file(void)
{
	char filename[BUFSIZ] = {0};
	char mid_path[BUFSIZ] = {0};
	std::string fupath;

	if (fd >= 0) {
		return fd;
	}

	sprintf(mid_path, "/%04d-%02d-%02d/%02d",
			cur_st.cur_year, cur_st.cur_mon,
			cur_st.cur_day, cur_st.cur_hour);
	fupath = this->path;
	fupath += mid_path;

	sprintf(filename, "%s/%s-%02d%02d",
			fupath.c_str(), tag.c_str(), 
			cur_st.cur_min, cur_st.cur_sec);
	check_path(fupath);

	/** 创建路径 **/
	if ((fd = open(filename, O_CREAT | O_RDWR, 0644)) < 0) {
		return -1;
	}
	return fd;
}


/**
 * 写文件
 * @IN:
 * 	sip: 源IP
 *	sport: 源port
 *	dip: 目的IP
 *	dport: 目的port
 *	data: HTTP-Header
 *	len: header-len
 **/
int ad_file_t::write_file(uint32_t sip, uint16_t sport, 
		uint32_t dip, uint16_t dport, char *data, int len)
{
	char buff[BUFSIZ];
	char sip_s[256];
	char dip_s[256];

	struct stat st;
	struct timeval tm;

	if (fd < 0) {
		fd = open_file();
	}

	inet_ntop(AF_INET, &sip, sip_s, sizeof(sip_s));
	inet_ntop(AF_INET, &dip, dip_s, sizeof(dip_s));

	sprintf(buff, "Src: %s:%d\r\n", sip_s, ntohs(sport));
	write(fd, buff, strlen(buff));

	sprintf(buff, "Dst: %s:%d\r\n", dip_s, ntohs(dport));
	write(fd, buff, strlen(buff));

	gettimeofday(&tm, NULL);

	sprintf(buff, "timestamp: %lu%lu\r\n", tm.tv_sec, tm.tv_usec/1000);
	write(fd, buff, strlen(buff));

	write(fd, data, len);

	/** 检查文件大小 **/
	if (fstat(fd, &st) != 0) {
		return -1;
	}

	/** 检查文件时间 **/
	if ( PATHSWITCH == ck_cur_st(cur_st) ){
		close(fd);
		fd = -1;
	}	

	if (st.st_size >= (int64_t)size) {
		close(fd);
		fd = -1;
	}

	return 0;
}

/**
 * 写文件
 * @IN:
 * 	sip: 源IP
 * 	sid: 搜索类型
 *	data: http报头 
 *	p_ua: useragent的偏移
 *	l_ua: ua的长度 
 *	p_key: 搜索关键字的偏移
 *	l_key: 搜索关键字的长度
 **/
int ad_file_t::write_file(uint32_t sip, int sid, char *data,
		int p_ua, int l_ua, int p_key, int l_key)
{
	char buff[BUFSIZ];
	char sip_s[256];

	struct stat st;

	if (fd < 0) {
		fd = open_file();
	}

	inet_ntop(AF_INET, &sip, sip_s, sizeof(sip_s));

	sprintf(buff, "Src: %s\tType: %d\tUA:", sip_s, sid);
	write(fd, buff, strlen(buff));

	write(fd, data + p_ua, l_ua);

	sprintf(buff, "\tKEY: ");
	write(fd, buff, strlen(buff));
	write(fd, data + p_key, l_key);
	write(fd, "\n", 1);

	/** 检查文件大小 **/
	if (fstat(fd, &st) != 0) {
		return -1;
	}

	/** 检查文件时间 **/
	if ( PATHSWITCH == ck_cur_st(cur_st) ){
		close(fd);
		fd = -1;
	}	

	if (st.st_size >= (int64_t)size) {
		close(fd);
		fd = -1;
	}

	return 0;
}

void ad_file_t::dump_pcap_init()
{
	pcap_desc = NULL;
	pout = NULL;
    memset(pcap_filename, 0, sizeof(pcap_filename));
}

int ad_file_t::dump_pcap(std::string dirname, const u_char *data, int data_len)
{
    struct pcap_pkthdr hdr;
    time_t now = time(NULL);
    struct tm tm_now = {0};
	struct stat st;
    char tmptime[16] = {0};
    char alter_dirname[16] = {0};
    char errbuf[PCAP_ERRBUF_SIZE] = {0};

    localtime_r(&now, &tm_now);
    strftime(tmptime, sizeof(tmptime) - 1, "%H%M%S", &tm_now);
    strftime(alter_dirname, sizeof(alter_dirname) - 1, "%Y-%m-%d/%H", &tm_now);
    pcap_path = dirname + "/" + alter_dirname;
    check_path(pcap_path);
    snprintf(pcap_filename, sizeof(pcap_filename), "%s/cache-%s.pcap", pcap_path.c_str(), tmptime);

	if (pcap_desc == NULL) {
		pcap_desc = pcap_open_offline(pcap_filename, errbuf);
	}
	if (pout == NULL) {
		pout = pcap_dump_open(pcap_desc, pcap_filename);
	}

	if (pcap_desc == NULL) {
		fprintf(stderr, "pcap_open_offline error: %s\n", errbuf);
		return -1;
	}

	if (pout == NULL) {
		fprintf(stderr, "pcap_dump_open error.\n");
		return -1;
	}

    gettimeofday(&hdr.ts, NULL);
    hdr.caplen = data_len;
    hdr.len = data_len;
    pcap_dump((u_char *)pout, &hdr, data);

	memset(&st, 0, sizeof(st));
	if (fstat(fd, &st) != 0) {
		return -1;
	}
	if (st.st_size >= (int64_t)size) {
		pcap_dump_close(pout);
		pcap_close(pcap_desc);
		pout = NULL;
		pcap_desc = NULL;
	}

	return 0;
}

void ad_file_t::dump_pcap_done()
{
	pcap_dump_close(pout);
	pcap_close(pcap_desc);
}
